介绍
static 是一个关键字,同时是一个修饰符。
static 可以修饰什么
- 属性
- 方法
- 代码块
- 内部类
修饰属性
用 static 修饰的属性称为 静态变量,类变量。
静态变量和非静态的变量的区别:
- 值的存储位置不同,非静态的变量存储在堆中,静态变量存储在方法区中。另外拓展,局部变量存储在栈中。
- 值的初始化(赋值)的时机不同。非静态的变量在创建实例对象的时候,在
<init>()
实例初始化方法中完成。静态变量的初始化在类初始化,<clinit>()
类初始化中完成。所以,静态变量的初始化比非静态的变量初始化要早。 - 非静态的变量每一个是独立的,每个类都有一份。而静态变量是所有类共享一份。
- 生命周期不同。非静态的变量,随着对象的创建而创建,当对象被垃圾回收器回收时就消失。而静态变量随着类的初始化而初始化,随着类的卸载而卸载。
- get/set 方法不同。非静态的变量对应的 get/set 也是非静态的。静态的变量对应的 get/set 也是静态的。
修饰方法
用 static 修饰的方法称为 静态方法或者类方法。
- 静态方法可以通过 “类名.方法名” 进行调用。当然也可以通过 “对象.方法名” 进行调用。
- 静态方法中是不可以出现 this,super 等关键字。
- 静态方法中不能直接使用本类的非静态的成员(属性,方法,内部类)。但是反过来可以,即,非静态的方法中可以调用静态的方法。
- 静态方法是不能被重写的。
修饰代码块
代码块的作用:为属性初始化。
代码块可以分为静态代码块和非静态代码块。非静态代码块的作用是为非静态的属性进行初始化。静态代码块的作用是为静态的属性进行初始化。
无论创建几个对象,静态代码块只执行一次(非静态代码块是每创建一个对象就会执行一次),如果有多个静态代码块,那么就按照顺序执行。
静态代码块优先于非静态代码块执行。在类的<clinit>()
初始化方法中执行。
<clinit>()
方法由两部分组成。分别是 静态变量的显式赋值 和 静态代码块。两者的执行顺序是谁在前面就谁先执行。在子类初始化的时候,如果发现父类还没有初始化,那么就会先初始化父类,即先执行父类的<clinit>()
方法。
类和实例初始化过程总结
先执行类的初始化。
1.1 如果有父类,那么就先执行父类的初始化(执行父类 static 相关的代码,例如,静态变量赋值,静态代码块等),在执行子类的初始化(执行子类 static 相关的代码,例如,静态变量赋值,静态代码块等)
实例初始化。
2.1 如果有父类,那么就先执行父类的实例初始化方法。即执行非静态属性和方法部分,二者谁在前面谁就先执行。然后在执行构造方法。父类执行完成之后在执行子类的初始化。
例子:
执行结果:
在子类的构造器中,如果没有 super,那么默认有一个 super(), 用于调用父类的无参构造。